`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file L2Cache.v
	@author Andrew D. Zonenberg
	@brief Dummy L2 cache. No actual cache, just an arbiter between the two L1s and the MMU.
 */
module L2Cache(
	clk,
	iside_addr, iside_rd,           iside_dout,                          iside_dinok,
	dside_addr, dside_rd, dside_wr, dside_dout, dside_wmask,  dside_din, dside_dinok,
	mmu_addr,   mmu_rd,   mmu_wr,   mmu_dout,   mmu_wmask,      mmu_din,   mmu_dinok
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Global
	input wire clk;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// I-side
	input wire[31:0] iside_addr;
	input wire iside_rd;
	output reg[31:0] iside_dout = 0;
	output reg iside_dinok = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// D-side
	input wire[31:0] dside_addr;
	input wire dside_rd;
	input wire dside_wr;
	input wire[3:0] dside_wmask;
	output reg[31:0] dside_dout = 0;
	input wire[31:0] dside_din;
	output reg dside_dinok = 0;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// MMU-side
	output reg[31:0] mmu_addr = 0;
	output reg[31:0] mmu_din = 0;
	input wire[31:0] mmu_dout;
	output reg[3:0] mmu_wmask = 0;
	output reg mmu_wr = 0;
	output reg mmu_rd = 0;
	input wire mmu_dinok;
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Mux logic
	
	wire dside_uncacheable;
	assign dside_uncacheable = (dside_addr[31:29] == 3'b101);
	
	//There are three possible read states we can be in:
	localparam READSTATE_NULL = 2'b00;		//No pending read
	localparam READSTATE_ISIDE = 2'b01;		//Pending read on I-side bus
	localparam READSTATE_DSIDE = 2'b10;		//Pending read on D-side bus
	//11 not used
	
	//CURRENT read state set by combinatorial logic
	reg[1:0] readstate = READSTATE_NULL;
	
	//Output muxing
	always @(dside_uncacheable, dside_wr, dside_rd, iside_rd, iside_addr, dside_addr, dside_din, dside_wmask) begin
		
		mmu_addr <= 0;
		mmu_din <= 0;
		mmu_wmask <= 0;
		mmu_wr <= 0;
		mmu_rd <= 0;
		readstate <= READSTATE_NULL;
		
		/*
			BIG PROBLEM!
			Boot ROM region is in kseg1 and hence uncacheable. But so are all the peripherals!
			Therefore, any access to peripherals while CPU is executing from boot ROM is going to result
			in a simultaneous miss on both ports. How to handle this?
		 */
		
		//Any operation, whether read or write, to an uncacheable address must occur without delay.
		//Note that uncacheable operations are only legal on the D-side bus except for boot ROM.
		//Writes are next priority. A write cannot be lost because D-side read and write cannot
		//occur simultaneously and I-side cannot write.
		if(dside_uncacheable || dside_wr) begin
			mmu_addr <= dside_addr;
			mmu_din <= dside_din;
			mmu_wmask <= dside_wmask;
			mmu_wr <= dside_wr;
			mmu_rd <= dside_rd;
			
			if(dside_rd)
				readstate <= READSTATE_DSIDE;
		end
				
		//Then D-side reads.
		//Needs to pre-empty instruction fetches to avoid causality hazards
		else if(dside_rd) begin
			mmu_addr <= dside_addr;
			mmu_din <= 32'h00000000;
			mmu_wmask <= 4'b0000;
			mmu_wr <= 1'b0;
			mmu_rd <= 1'b1;
			
			readstate <= READSTATE_DSIDE;
		end
				
		else if(iside_rd) begin
			mmu_addr <= iside_addr;
			mmu_din <= 32'h00000000;
			mmu_wmask <= 4'b0000;
			mmu_wr <= 1'b0;
			mmu_rd <= 1'b1;
			
			readstate <= READSTATE_ISIDE;
		end
		
	end
	
	//Buffer read state so it's ready when the response comes back
	reg[1:0] readstate_buf = READSTATE_NULL;
	always @(posedge clk) begin
		readstate_buf <= readstate;
	end
	
	//Response muxing
	always @(readstate_buf, mmu_dout, mmu_dinok) begin

		if(readstate_buf == READSTATE_ISIDE) begin
			iside_dout <= mmu_dout;
			iside_dinok <= mmu_dinok;
			
			dside_dout <= 0;
			dside_dinok <= 0;
		end
		
		else if(readstate_buf == READSTATE_DSIDE) begin
			iside_dout <= 0;
			iside_dinok <= 0;
			
			dside_dout <= mmu_dout;
			dside_dinok <= mmu_dinok;
		end
		
		else begin
			iside_dout <= 0;
			iside_dinok <= 0;
			
			dside_dout <= 0;
			dside_dinok <= 0;
		end

	end

endmodule
